package com.ruoyi.base.service.impl;

import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.ruoyi.base.domain.Orders;
import com.ruoyi.base.domain.WxPayLog;
import com.ruoyi.base.domain.client.vx.WxPrepayWithRequestPaymentResponse;
import com.ruoyi.base.domain.client.vx.WxRequestBody;
import com.ruoyi.base.domain.dto.WechatRequestPayAttachDTO;
import com.ruoyi.base.service.IWxPayLogService;
import com.ruoyi.common.enums.base.PaymentStatus;
import com.wechat.pay.java.core.exception.MalformedMessageException;
import com.wechat.pay.java.core.exception.ValidationException;
import com.wechat.pay.java.core.http.Constant;
import com.wechat.pay.java.core.notification.NotificationConfig;
import com.wechat.pay.java.core.notification.NotificationParser;
import com.wechat.pay.java.core.notification.RequestParam;
import com.wechat.pay.java.core.util.GsonUtil;
import com.wechat.pay.java.service.partnerpayments.jsapi.model.Transaction;
import com.wechat.pay.java.service.payments.jsapi.model.Payer;
import com.ruoyi.base.service.IOrdersService;
import com.ruoyi.base.service.IWechatPayService;
import com.ruoyi.common.config.WxPayProperties;
import com.wechat.pay.java.core.Config;
import com.wechat.pay.java.service.payments.jsapi.JsapiService;
import com.wechat.pay.java.service.payments.jsapi.model.Amount;
import com.wechat.pay.java.service.payments.jsapi.model.PrepayRequest;
import com.wechat.pay.java.service.payments.jsapi.model.PrepayResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Service;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.DefaultTransactionDefinition;

import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.*;
import java.security.Signature;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import cn.hutool.extra.servlet.ServletUtil;

import static com.ruoyi.common.enums.base.TransactionType.INCOME;

/**
 * @ClassName WechatPayServiceImpl
 * @Description TODO
 * @Author Mr、Liu
 * @Date 2025/6/25 23:05
 * @Version 1.0
 */

@Slf4j
@Service
public class WechatPayServiceImpl implements IWechatPayService {


    @Autowired
    private Config config;

    @Autowired
    private IOrdersService iOrdersService;

    @Autowired
    private WxPayProperties properties;

    @Autowired
    private DataSourceTransactionManager manager;
    @Autowired
    private IWxPayLogService iWxPayLogService;




    @Override
    @Transactional //事务注解
    public WxPrepayWithRequestPaymentResponse unifiedOrder(WxRequestBody requestBody) throws Exception {

        log.info("***微信支付统一下单***");
        String tradeNo = requestBody.getTradeNo();
        // 1. 校验订单是否存在
        Optional<Orders> optById = iOrdersService.getOptById(tradeNo);
        if (!optById.isPresent()) {
            throw new RuntimeException("订单不存在");
        }
        Orders orders = optById.get();

        // 2.初始化微信支付服务
        JsapiService service = new JsapiService.Builder().config(config).build();

        //5. 构造支付请求参数
        PrepayRequest request = new PrepayRequest();
        //(1)设置支付金额
        Amount amount = new Amount();
        BigDecimal curprice = orders.getPaidPrice();
        // 验证金额有效性
        if (curprice == null || curprice.compareTo(BigDecimal.ZERO) <= 0) {
            throw new IllegalArgumentException("Invalid price value");
        }

        // 转换为分并四舍五入
        int total = curprice.multiply(new BigDecimal(100)).setScale(0, RoundingMode.HALF_UP).intValueExact();

        // 微信支付要求金额范围校验（1分-约3.3万元）
        if (total < 1 || total > 33000000) {
            throw new IllegalArgumentException("Amount out of wechat pay range");
        }
        log.info("***支付金额:{}分",total);
        amount.setTotal(total);
        request.setAmount(amount);
        //(2)设置商户和APP信息
        request.setAppid(properties.getAppId());
        request.setMchid(properties.getMchId());

        //(3)设置回调地址
        request.setDescription(orders.getProductName() + "云地图"); // 商品名称
        request.setNotifyUrl(properties.getPayNotifyUrl()); //回调地址

        //(6) 生成唯一订单号
        String wxOrder = tradeNo + "_" + new Date().getTime() / 1000;

        orders.setWechatOrderId(wxOrder);
        request.setOutTradeNo(wxOrder);

        //(7) 设置支付用户
        Payer payer = new Payer();
        payer.setOpenid(orders.getUserId());
        request.setPayer(payer);

        // 交易记录表添加请求数据
        WxPayLog payLog = new WxPayLog();
        payLog.setOpenid(orders.getUserId());
        payLog.setTransactionId(wxOrder);
        payLog.setCurprice(Long.valueOf(total));
        payLog.setPayType(String.valueOf(INCOME.getCode()));
        payLog.setRequestBody(JSON.toJSONString(request));
        payLog.setOrderId(tradeNo);

        log.info("小程序用户openid：{}，微信支付请求参数:{}"
                , orders.getUserId(), JSON.toJSONString(payLog));
        int i = iOrdersService.updateOrders(orders);
        if (i !=1){
            throw new RuntimeException("微信订单更新失败");
        }
        Boolean saveBool = iWxPayLogService.insertWxPayLog(payLog) == 1 ? true : false;
        if (!saveBool) {
            throw new RuntimeException("微信支付记录保存失败");
        }


        //6. 调用微信支付API
        PrepayResponse response = service.prepay(request);

        //8. 生成支付签名
        String uuid = UUID.randomUUID().toString().replace("-", "");


        String date = String.valueOf(new Date().getTime() / 1000);
        String signatureStr = Stream.of(properties.getAppId(), date, uuid, "prepay_id=" + response.getPrepayId())
                .collect(Collectors.joining("\n", "", "\n"));
        Signature sign = Signature.getInstance("SHA256withRSA");
        sign.initSign(properties.getPrivateKey());
        sign.update(signatureStr.getBytes(StandardCharsets.UTF_8));
        String str = Base64.getEncoder().encodeToString(sign.sign());

        //9. 返回支付参数
        WxPrepayWithRequestPaymentResponse wxPayResp = WxPrepayWithRequestPaymentResponse.builder()
                .appid(properties.getAppId())
                .nonceStr(uuid)
                .partnerid(properties.getMchId())
                .prepayid(response.getPrepayId())
                .sign(str)
                .timestamp(date)
                .build();

        return wxPayResp;
    }

    /**
     * @param request
     * @return
     */
    @Override
    public Map<String, Object> payNotify(HttpServletRequest request) {
        log.info("****支付成功回调*****");
        // 构造 RequestParam
        RequestParam requestParam = requestParamByServletRequest(request);
        // 初始化 NotificationParser
        NotificationParser parser = new NotificationParser((NotificationConfig) config);
        // 以支付通知回调为例，验签、解密并转换成 Transaction
        Transaction transaction = null;
        try {
            transaction = parser.parse(requestParam, Transaction.class);
        } catch (MalformedMessageException e) {
            log.error("验签失败，解析微信支付应答或回调报文异常，返回信息：", e);
            return buildWxNotifyResp(Boolean.FALSE);
        } catch (ValidationException e) {
            log.error("验签失败，验证签名失败，返回信息：", e);
            return buildWxNotifyResp(Boolean.FALSE);
        } catch (Exception e) {
            log.error("验签失败，返回信息：", e);
            return buildWxNotifyResp(Boolean.FALSE);
        }
        log.info("验签成功！-支付回调结果：{}", transaction.toString());
        // 从 attach 中提提交订单ID
        String outTradeNo = transaction.getOutTradeNo();
        log.info("微信订单号:{}" , outTradeNo);
        //查询对应的支付记录日志
        WxPayLog payLog = iWxPayLogService.selectWxPayLogByTransactionId(outTradeNo);
        log.info("支付日志:{}" , payLog.toString());
        payLog.setPayType(String.valueOf(INCOME.getCode()));
        payLog.setTransactionId(transaction.getTransactionId());
        payLog.setResponseBody(JSON.toJSONString(transaction));
        payLog.setStatus(String.valueOf(PaymentStatus.SUCCESS.getCode()));

        // 获取到了当前订单的订单ID
        String orderId = payLog.getOrderId();
        Orders orders = new Orders();
        orders.setId(orderId);
        //开启事务
        DefaultTransactionDefinition definition = new DefaultTransactionDefinition();
        definition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
        TransactionStatus localTransactionStatus = manager.getTransaction(definition);
        //处理支付结果
        try {
            // 判断订单状态
            if (Transaction.TradeStateEnum.SUCCESS != transaction.getTradeState()) {
                log.info("订单号【{}】支付未成功", transaction.getTransactionId());
                payLog.setStatus(String.valueOf(PaymentStatus.FAILED.getCode()));
                orders.setPaymentStatus(String.valueOf(PaymentStatus.FAILED.getCode()));
            } else {
                payLog.setPayNotifyTime(new Date());
                orders.setPaymentStatus(String.valueOf(PaymentStatus.SUCCESS.getCode()));
            }
            Boolean updatePayLogBool = iWxPayLogService.updateById(payLog);
            Boolean updateOrderRecordBool = iOrdersService.updateById(orders);
            if (updatePayLogBool && updateOrderRecordBool) {
                manager.commit(localTransactionStatus);
                return buildWxNotifyResp(Boolean.TRUE);
            } else {
                log.error("内部订单号：{},微信支付订单号：{}，更新失败wx_pay_log更新状态：{}"
                        , transaction.getTransactionId()
                        , updatePayLogBool);
                manager.rollback(localTransactionStatus);
                return buildWxNotifyResp(Boolean.FALSE);
            }
        } catch (Exception e) {
            log.error("内部订单号：{},微信支付订单号：{}，内部异常：{}"
                    , transaction.getTransactionId(), e);
            manager.rollback(localTransactionStatus);
            return buildWxNotifyResp(Boolean.FALSE);
        }
    }


    /**
     * 根据ServletRequest获取验签参数RequestParam用于微信通知解析器
     *
     * @param request
     * @return
     */
    private RequestParam requestParamByServletRequest(HttpServletRequest request) {
        // 请求头Wechatpay-Signature
        String signature = request.getHeader(Constant.WECHAT_PAY_SIGNATURE);
        // 请求头Wechatpay-nonce
        String nonce = request.getHeader(Constant.WECHAT_PAY_NONCE);
        // 请求头Wechatpay-Timestamp
        String timestamp = request.getHeader(Constant.WECHAT_PAY_TIMESTAMP);
        // 微信支付证书序列号
        String serial = request.getHeader(Constant.WECHAT_PAY_SERIAL);
        // 签名方式
        String signType = request.getHeader("Wechatpay-Signature-Type");
        // 构造 RequestParam
        RequestParam requestParam = new RequestParam.Builder()
                .serialNumber(serial)
                .nonce(nonce)
                .signature(signature)
                .timestamp(timestamp)
                .signType(signType)
                .body(ServletUtil.getBody(request))
                .build();
        String body = requestParam.getBody();
        log.info("微信回调body信息：{}", body);
        log.info("验签参数：{}", GsonUtil.toJson(requestParam));
        return requestParam;
    }


    private Map<String, Object> buildWxNotifyResp(boolean success) {
        Map<String, Object> resultMap = new HashMap<>();
        if (success) {
            resultMap.put("code", 200);
            resultMap.put("message", "ok");
        } else {
            resultMap.put("code", 500);
            resultMap.put("message", "失败");
        }
        return resultMap;
    }
}
